package net.tinyportal.service;

import java.io.File;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.portlet.GenericPortlet;
import javax.servlet.ServletConfig;
import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;

import net.tinyportal.Constant;
import net.tinyportal.javax.portlet.TpPortletConfig;
import net.tinyportal.javax.portlet.TpPortletPreference;
import net.tinyportal.portal.PortletHolder;
import net.tinyportal.tools.PortletXML;

import org.jdom.Document;
import org.jdom.Element;
import org.jdom.Namespace;
import org.jdom.input.SAXBuilder;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.web.context.ServletConfigAware;

public class PortletService implements ServletConfigAware, ApplicationContextAware  {

	@Autowired
	ServletContext context;

	@Autowired
	ApplicationContext applicationContext;
	
	@Autowired
	PreferenceService preferenceService;

	/* Map contenant les portletsName associé au portletHolder */
	Map<String, PortletHolder> portletPool = new HashMap<String, PortletHolder>();

	/* Map qui contient les portletsName en vis a vis des portletsPath */
	Map<String, String> portletPathMap = new HashMap<String, String>();



	public Set<String> getPortletSet() {
		return portletPool.keySet();
	}

	/* Retourne un portletHolder a partir du nom du portlet */
	public PortletHolder getPortlet(String portletName) {
		return portletPool.get(portletName);
	}
	
	/*
	 * On recupere soit le portlet de la session, soit un nouveau portlet qui est alors mis en session
	 */
	public PortletHolder getPortlet(HttpServletRequest request, String portletName) {
		HttpSession session = request.getSession(true);
		PortletHolder bean = (PortletHolder)session.getAttribute(portletName);
		if (bean == null) {
			PortletHolder legacy = portletPool.get(portletName);
			session.setAttribute(portletName, bean = legacy.clone());
		}
		return bean;
	}


	public String getPortletPath(String portletName) {
		return portletPathMap.get(portletName);
	}

	/**
	 * Retourne le nom du type de portlet a partir du nom de l'instance de celui-ci
	 * @param portletInstanceName nom instancié du portlet
	 * @return nom du type de portlet
	 */
	public String getPortletName(String portletInstanceName) {
		if (portletInstanceName.contains("@@"))
			return portletInstanceName.split("@@")[0];
		return portletInstanceName;
	}

	public String getPortletTypeForId(String portletId) {
		for (String portletName : portletPool.keySet()) {
			if (portletPool.get(portletName).getPortletId().equals(portletId)) {
				return getPortletName(portletName);
			}
		}
		return null;
	}

	/* Chargement des portlets */

	@Override
	public void setServletConfig(ServletConfig config) {
		this.context = config.getServletContext();
		String portletsPath = context.getRealPath("") + Constant.portlets_path;

		File directoryPortletsPath = new File(portletsPath);
		if (!(directoryPortletsPath.exists() && directoryPortletsPath.isDirectory())) {
			//on lève une exception
		}

		/*
		 * On parcourt les répertoires pour s'intéresser à ceux etant des portlets
		 */
		for (File directory : directoryPortletsPath.listFiles()) {
			if ((directory.isDirectory()) && (new File(directory+ Constant.portlet_xml).exists())) {
				try {
					loadPortlets(directory);
				} catch (Exception e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		}
	}

	/**
	 * Charge l'ensemble des portlets définit dans le fichier {directory}/WEB-INF/portlet.xml
	 * @param directory répertoire du(des) portlet(s)
	 * @throws Exception 
	 */
	public void loadPortlets(File directory) throws Exception {
		if (!directory.isDirectory()) throw new Exception(directory.getName() + " n'est pas un répertoire");

		File portlet_xml = new File(directory+ Constant.portlet_xml);
		if (!portlet_xml.exists()) throw new Exception(directory.getName() + " n'est pas un portlet");
		/*
		 * On initialise le classpath du portlet
		 */
		List<URL> urls = new ArrayList<URL>();
		StringBuffer portlet_path_classes = new StringBuffer(directory.toString()).append(Constant.portlet_classes);
		StringBuffer portlet_path_lib = new StringBuffer(directory.toString()).append(Constant.portlet_lib);
		urls.add(new File(portlet_path_classes.toString()).toURI().toURL());
		File portletLibPath = new File(portlet_path_lib.toString());
		File[] portletLibs = portletLibPath.listFiles();
		if (portletLibs!=null)
			for (File portletLib : portletLibs)
				if (portletLib.getName().endsWith(Constant.jar_extension)) {
					urls.add(portletLib.toURI().toURL());
					System.out.println("Ajout " + portletLib.toURI().toURL());
				}
		URL url[] = (URL[])urls.toArray(new URL[0]);
		URLClassLoader loader = new URLClassLoader(url,Thread.currentThread().getContextClassLoader());

		List<TpPortletConfig> portletsConfig = parse(portlet_xml);

		for (TpPortletConfig portletConfig:portletsConfig) {
			preferenceService.addPreference(portletConfig.getPortletName(),portletConfig.getPortletPreference());
			portletConfig.setLoader(loader);
			portletConfig.setPortletPath(directory.toString().substring(context.getRealPath("").length()).replaceAll("\\\\", "/"));

			loadPortlet(portletConfig);

		}
	}

	public void loadPortlet(String portletName) {

		TpPortletConfig portletConfig = getPortlet(portletName.substring(0, portletName.indexOf("//@"))
				).getPortletConfig();
		loadPortlet(portletConfig, portletName);
	}

	public void loadPortlet(TpPortletConfig portletConfig) {
		loadPortlet(portletConfig, null);
	}

	public void loadPortlet(TpPortletConfig portletConfig, String portletName) {
		try {
			/*
			 * On charge le portlet
			 */
			ClassLoader loader = portletConfig.getLoader();
			if (loader==null) {

			}

			@SuppressWarnings("rawtypes")
			Class c = loader.loadClass(portletConfig.getPortletClass());
			GenericPortlet portlet = (GenericPortlet)c.newInstance();
			applicationContext.getAutowireCapableBeanFactory().autowireBean(portlet);
			portlet.init(portletConfig);

			PortletHolder portletHolder = new PortletHolder(portlet);

			/*
			 * On effectue les liaisons Spring si besoin
			 */
			applicationContext.getAutowireCapableBeanFactory().autowireBean(portletHolder);

			portletPool.put((portletName == null)?portletHolder.getPortletName():portletName, portletHolder);
		} catch (Exception pe) {
			//Exception levée lors de l'initialisation du portlet, on passe
			// la suite du traitement du portlet
			//			return;
			pe.printStackTrace();
		}
	}

	/**
	 * Parse le fichier portlet.xml et retourne un ensemble de ...
	 * @param xmlDocument
	 * @return
	 * @throws Exception
	 */
	public List<TpPortletConfig> parse(File xmlDocument) throws Exception{
		try {
			File portletPath = xmlDocument.getParentFile().getParentFile();

			//On crée une instance de SAXBuilder
			SAXBuilder sxb = new SAXBuilder();
			//On crée un nouveau document JDOM avec en argument le fichier XML
			//Le parsing est terminé ;)
			Document document = sxb.build(xmlDocument);


			//On initialise un nouvel élément racine avec l'élément racine du document.
			Element racine = document.getRootElement();

			Namespace portletNS = racine.getNamespace();
			List<Element> portletsElement = racine.getChildren("portlet",portletNS);
			List<TpPortletConfig> portletsConfig = new ArrayList<TpPortletConfig>(); 
			for (Element portletElement : portletsElement) {
				PortletXML portletXml = new PortletXML(portletElement, portletNS, portletPath);
				TpPortletConfig tpPortletConfigXML = portletXml.getPortletConfig();
				portletPathMap.put(portletXml.getPortletName(), portletXml.getPortletPath());
				context.setAttribute("net.tinyportal.spring.portlet", tpPortletConfigXML.getPortletName());				
				TpPortletConfig tpPortletConfig = this.applicationContext.getBean(TpPortletConfig.class);
				tpPortletConfig.setSecurity(portletXml.getSecurity());
				tpPortletConfig.setBundleBaseName(portletXml.getBundle());
				tpPortletConfig.setMimeType(portletXml.getMimeType());
				tpPortletConfig.setPortletInfo(portletXml.getPortletInfo());
				tpPortletConfig.setPortletModes(portletXml.getPortletModes());
				tpPortletConfig.setWindowStates(portletXml.getWindowsStates());
				tpPortletConfig.setPorletName(portletXml.getPortletName());
				TpPortletPreference p = new TpPortletPreference(portletXml.getPortletPreference(),portletXml.getReadOnlyPreferences());
				applicationContext.getAutowireCapableBeanFactory().autowireBean(p);
				
				
				tpPortletConfig.setPortletPreference(p);
				tpPortletConfig.setPortletClass(portletXml.getPortletClass());
				portletsConfig.add(tpPortletConfig);
				context.removeAttribute("net.tinyportal.spring.portlet");
			}
			return portletsConfig;
		}
		catch(Exception e){
			e.printStackTrace();
			throw e;
		}	      
	}

	@Override
	public void setApplicationContext(ApplicationContext applicationContext)
			throws BeansException {
		this.applicationContext = applicationContext;
	}

}
